package com.nuoniu.sibanyun.service.production.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nuoniu.sibanyun.common.exception.NuoNiuException;
import com.nuoniu.sibanyun.entity.dto.BOMDetailDto;
import com.nuoniu.sibanyun.entity.erp.ErpNumberSet;
import com.nuoniu.sibanyun.entity.production.*;
import com.nuoniu.sibanyun.entity.vo.*;
import com.nuoniu.sibanyun.mapper.production.VProductionDispatchMainMapper;
import com.nuoniu.sibanyun.service.erp.IErpAccountService;
import com.nuoniu.sibanyun.service.erp.IErpNumberSetService;
import com.nuoniu.sibanyun.service.erp.in.IInInventoryInfoService;
import com.nuoniu.sibanyun.service.erp.in.IInInventoryService;
import com.nuoniu.sibanyun.service.production.*;
import com.nuoniu.sibanyun.userThread.UserInfo;
import com.nuoniu.sibanyun.userThread.UserThreadLocal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 生产制造——派工单 服务实现类
 * </p>
 *
 * @author 小啊刚
 * @since 2021-04-19
 */
@Service
public class VProductionDispatchMainServiceImpl extends ServiceImpl<VProductionDispatchMainMapper, VProductionDispatchMain> implements IVProductionDispatchMainService {

    @Autowired
    private IErpAccountService accountService;
    @Autowired
    private IVProductionWorkDetailService productionWorkDetailService;
//    @Autowired
//    private IVProductionWorkInventoryDetailService inventoryDetailService;
    @Autowired
    private IVProductionWorkProcedureDetailService procedureDetailService;
    @Autowired
    private IVBomMainService bomMainService;
    @Autowired
    private IVBomDetailService bomDetailService;
    @Autowired
    private IInInventoryService inInventoryService;
    @Autowired
    private IInInventoryInfoService inInventoryInfoService;
    @Autowired
    private IErpNumberSetService erpNumberSetService;
    @Autowired
    private IErpWorkProcedureCardDetailService erpWorkProcedureCardDetailService;


    @Override
    public String getCode() {
        UserInfo userInfo = UserThreadLocal.get();
        ErpNumberSet numberSet = erpNumberSetService.getByCode("DispatchJob");
        String dateStr = numberSet.getMedian() == 1 ? "%Y-%m-%d" : "%Y-%m";
        String code = baseMapper.getNewestCode(userInfo.getCompanyId(), dateStr);
        return erpNumberSetService.getCode(numberSet, code);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveDispatch(VProductionDispatchMain productionDispatchMain) {
        LocalDateTime now = LocalDateTime.now();
        UserInfo userInfo = UserThreadLocal.get();
        Integer currentAccountId = accountService.getCurrentAccountId();
        Integer productionWorkMainId = productionDispatchMain.getProductionWorkMainId();
        Integer productionWorkDetailId = productionDispatchMain.getProductionWorkDetailId();
        if (null == productionWorkMainId) {
            throw new NuoNiuException("请选择加工单");
        }
        if (null == productionWorkDetailId) {
            throw new NuoNiuException("请选择加工单产品明细");
        }

        Long mainId = productionDispatchMain.getId();
        BigDecimal offset = new BigDecimal(0);
        if(null == mainId){
            String code = getCode();
            if(code == null) {
                throw new NuoNiuException("单据编号已上限");
            }
            productionDispatchMain.setVoucherCode(code);
            productionDispatchMain.setVoucherState(0);
            offset = offset.add(productionDispatchMain.getDispatchInventoryQuantity());
            productionDispatchMain.setCompanyId(userInfo.getCompanyId());
            productionDispatchMain.setAccountId(currentAccountId);
            productionDispatchMain.setCreateUserId(userInfo.getUserId());
            productionDispatchMain.setCreateUserName(userInfo.getRealName());
            productionDispatchMain.setCreateDate(now);
        }else{
            VProductionDispatchMain oldVProductionDispatchMain = getById(mainId);
            if (null == oldVProductionDispatchMain) {
                throw new NuoNiuException("查询不到记录");
            }
            if(oldVProductionDispatchMain.getVoucherState() > 0) {
                throw new NuoNiuException("该单据已审核，不能修改");
            }
            offset = offset.subtract(oldVProductionDispatchMain.getDispatchInventoryQuantity()).add(productionDispatchMain.getDispatchInventoryQuantity());


        }
        List<VProductionWorkProcedureDetail> procedureDetails = productionDispatchMain.getProcedureDetails();
        if (null == procedureDetails || procedureDetails.size() == 0) {
            throw new NuoNiuException("工序明细不能为空");
        }
        boolean isProcedureDetailsNull = procedureDetails.stream().anyMatch(pd -> null == pd.getWorkerId() || null == pd.getProcedureId() || null == pd.getStartDate());
        if (isProcedureDetailsNull) {
            throw new NuoNiuException("派工明细 必填数据 不完整，请检测");
        }
        // 新增派工单时，校验 派工明细ID不能有值
        if(null == mainId ){
            boolean isHaveId = procedureDetails.stream().anyMatch(f -> null != f.getId());
            if (isHaveId) {
                throw new NuoNiuException("派工明细，ID参数错误");
            }
        }
        boolean b = saveOrUpdate(productionDispatchMain);
        //更新 加工单产品明细 累计派遣量
        productionWorkDetailService.updateCumDispatchQuantity(productionDispatchMain.getProductionWorkDetailId(),offset);

        if (b){
            /** 派遣明细 */
            List<Long> procedureDetailIds = new ArrayList<>();
            procedureDetails.forEach(procedureDetail->{
//                procedureDetail.setProductionDispatchId(productionDispatchMain.getId().intValue());
//                procedureDetail.setProductionDispatchCode(productionDispatchMain.getVoucherCode());
                procedureDetail.setCompanyId(userInfo.getCompanyId());
                procedureDetail.setAccountId(currentAccountId);
                procedureDetail.setStatus(0);
                procedureDetail.setParentInventoryId(productionDispatchMain.getInventoryId());
                if (null != procedureDetail.getId()){
                    procedureDetailIds.add(procedureDetail.getId());
                }
            });
            if(null != mainId){
                QueryWrapper<VProductionWorkProcedureDetail> wrapper = new QueryWrapper();
                wrapper.eq("production_dispatch_id",productionDispatchMain.getId());
                //先删除派工明细
                if (procedureDetailIds.size() > 0){
                    wrapper.notIn("id", procedureDetailIds);
                }
                procedureDetailService.remove(wrapper);
            }
            procedureDetailService.saveOrUpdateBatch(procedureDetails);
        }
    }

    @Override
    public VProductionDispatchMainVo getOneByCondition(Integer actionType, Long currentId) {
        VProductionDispatchMainVo productionDispatchMainVo = new VProductionDispatchMainVo();
        UserInfo userInfo = UserThreadLocal.get();
        Integer currentAccountId = accountService.getCurrentAccountId();
        VProductionDispatchMain productionDispatchMain = null;
        switch (actionType){
            case 1:
                if(null != currentId){
                    productionDispatchMain = baseMapper.getPreId(userInfo.getCompanyId(), currentAccountId,currentId);break;
                }else{
                    productionDispatchMain = baseMapper.getFirstId(userInfo.getCompanyId(), currentAccountId);break;
                }
            case 2:
                if(null != currentId){
                    productionDispatchMain = baseMapper.getNextId(userInfo.getCompanyId(), currentAccountId,currentId);break;
                }else{
                    productionDispatchMain = baseMapper.getLastId(userInfo.getCompanyId(), currentAccountId);break;
                }
            case 3: productionDispatchMain = baseMapper.getFirstId(userInfo.getCompanyId(), currentAccountId);break;
            case 4: productionDispatchMain = baseMapper.getLastId(userInfo.getCompanyId(), currentAccountId);break;
            default:
                if(null != currentId){
                    productionDispatchMain = getById(currentId);break;
                }else{
                    productionDispatchMain = baseMapper.getLastId(userInfo.getCompanyId(), currentAccountId);break;
                }
        }
        if (null == productionDispatchMain) {
            return productionDispatchMainVo;
        }
        productionDispatchMainVo.setVProductionDispatchMain(productionDispatchMain);

//        List<VProductionWorkInventoryDetail> inventoryDetailList = inventoryDetailService.listByProductionDispatchId(productionDispatchMain.getId());
        List<VProductionWorkProcedureDetailVo> procedureDetailVoList = procedureDetailService.listByProductionDispatchId(productionDispatchMain.getId());
//        productionDispatchMainVo.setInventoryDetails(inventoryDetailList);
        productionDispatchMainVo.setProcedureDetails(procedureDetailVoList);
        return productionDispatchMainVo;
    }

    @Override
    public void updateFinishInventoryQuantity(Long id, BigDecimal finishInventoryQuantity) {
        VProductionDispatchMain productionDispatchMain = new VProductionDispatchMain();
        productionDispatchMain.setId(id);
        productionDispatchMain.setFinishInventoryQuantity(finishInventoryQuantity);
        updateById(productionDispatchMain);
    }

    @Override
    public void listLeafDetailByMother(List<VProductionWorkInventoryDetail> inventoryDetails, BigDecimal multiple, Long motherBomId,boolean isGetAvailableQuantity,boolean isSingle) {
       /** 根据BOMid  查询物料明细*/
        List<ErpBomDetailVo> erpBomDetailVos = bomDetailService.listErpBomDetailVo(motherBomId);
//        List<ErpBomDetail> childBomList = bomMainService.getChildBomList(motherBomId);
        HashMap<Integer, BigDecimal> exitAvailableQuantityMap = new HashMap<>();
        erpBomDetailVos.forEach(erpBomDetail -> {
            Integer bomId = erpBomDetail.getBomId();
            /** 查询当前物料明细的属性 */
            InInventoryPropertyVo inInventoryPropertyVo = inInventoryService.getInInventoryPropertyVo(erpBomDetail.getInventoryId());
            BigDecimal newMultiple = erpBomDetail.getBaseQuantity().multiply(multiple).setScale(4,BigDecimal.ROUND_UP);
            /** 如果是成件套,则直接计算 单阶 ，不管是否需要展开BOM */
            if(inInventoryPropertyVo.getIsSuite()){
                getWorkInventoryDetail(inventoryDetails,erpBomDetail,newMultiple,isGetAvailableQuantity,exitAvailableQuantityMap);
            }
            /** 如果是虚拟件,则直接展开下一级BOM，但不会继续展开以后的BOM层级，不管是否需要展开BOM */
            else if(inInventoryPropertyVo.getIsInventedKey()){
                if(null == bomId) {
                    throw new NuoNiuException("请选择配置好虚拟件的BOM");
                }
                listLeafDetailByMother(inventoryDetails, newMultiple, bomId.longValue(),isGetAvailableQuantity,true);
            }
            else if (null != bomId && !isSingle){
                listLeafDetailByMother(inventoryDetails, newMultiple, bomId.longValue(),isGetAvailableQuantity,false);
            }
            else{
                getWorkInventoryDetail(inventoryDetails,erpBomDetail,newMultiple,isGetAvailableQuantity,exitAvailableQuantityMap);
            }
        });
    }

    /**
     * 组装 材料明细
     * @param inventoryDetails          返回的材料明细
     * @param erpBomDetail
     * @param newMultiple               基础需要数量
     * @param isGetAvailableQuantity    是否获取 可用量
     * @param exitAvailableQuantityMap
     */
    private void getWorkInventoryDetail(List<VProductionWorkInventoryDetail> inventoryDetails,ErpBomDetailVo erpBomDetail, BigDecimal newMultiple,boolean isGetAvailableQuantity,HashMap<Integer, BigDecimal> exitAvailableQuantityMap){
        Integer inventoryId = erpBomDetail.getInventoryId();
        //计算 需求系数
        VProductionWorkInventoryDetail inventoryDetail = new VProductionWorkInventoryDetail();
        inventoryDetail.setInventoryId(inventoryId);
        inventoryDetail.setInventoryName(erpBomDetail.getInventoryName());
        inventoryDetail.setInventoryCode(erpBomDetail.getInventoryCode());
        inventoryDetail.setInventorySpecification(erpBomDetail.getSpecification());
        inventoryDetail.setUnitId(erpBomDetail.getUnitId());
        inventoryDetail.setUnitGroupId(erpBomDetail.getUnitGroupId());
        inventoryDetail.setBaseUnit(erpBomDetail.getBaseUnit());
        inventoryDetail.setBaseUnitName(erpBomDetail.getBaseUnitName());
        inventoryDetail.setSubUnit(erpBomDetail.getSubUnit());
        inventoryDetail.setSubUnitName(erpBomDetail.getSubUnitName());
        inventoryDetail.setUnitExchangeRate(erpBomDetail.getUnitExchangeRate());
        inventoryDetail.setBomId(erpBomDetail.getOrderId());
        inventoryDetail.setBomName(erpBomDetail.getParentBomVersion());
        inventoryDetail.setRequireQuantity(newMultiple);
        inventoryDetail.setBaseRequireQuantity(newMultiple);
        BigDecimal unitExchangeRate = erpBomDetail.getUnitExchangeRate();
        if (null != unitExchangeRate && unitExchangeRate.compareTo(BigDecimal.ZERO) > 0){
            inventoryDetail.setSubRequireQuantity(newMultiple.divide(unitExchangeRate,4,BigDecimal.ROUND_UP));
            inventoryDetail.setSubBaseRequireQuantity(inventoryDetail.getSubRequireQuantity());
        }
        inventoryDetail.setLossRate(new BigDecimal(null == erpBomDetail.getLossRate() ? 0 : erpBomDetail.getLossRate()));
        //获取 现存量
        BigDecimal existQuantity = inInventoryInfoService.getExistQuantity(inventoryId);
        if (null != existQuantity){
            inventoryDetail.setExistingQuantity(existQuantity);
        }
        if (isGetAvailableQuantity){
            BigDecimal availableQuantity = exitAvailableQuantityMap.get(inventoryId);
            if (null == availableQuantity){
                //计算 可用量
                HashSet<Integer> idSet = new HashSet<>();
                idSet.add(inventoryId);
                HashMap<Integer, BigDecimal> availableQuantityMap = inInventoryService.getAvailableQuantityMap(idSet,null);
                availableQuantity = availableQuantityMap.get(inventoryId);
                if (null != availableQuantity){
                    //累加 现存量
                    if (null != existQuantity){
                        availableQuantity = availableQuantity.add(existQuantity);
                    }
                    exitAvailableQuantityMap.put(inventoryId,availableQuantity);
                }
            }
            if (null != availableQuantity){
                inventoryDetail.setAvailableQuantity(availableQuantity);
            }
        }
        inventoryDetails.add(inventoryDetail);
    }

    @Override
    public List<VProductionWorkInventoryDetail> listLeafDetailByMother(BOMDetailDto bomDetailDto) {
        Long motherBomId = bomDetailDto.getMotherBomId();
        boolean isGetAvailableQuantity = bomDetailDto.getIsGetAvailableQuantity();
        boolean isSingle = bomDetailDto.getIsSingle();
        List<VProductionWorkInventoryDetail> workInventoryDetailList = new ArrayList<>();
        listLeafDetailByMother(workInventoryDetailList,new BigDecimal(1),motherBomId,isGetAvailableQuantity,isSingle);
        return workInventoryDetailList;
    }


    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void deleteById(Long id) {
        UserInfo userInfo = UserThreadLocal.get();
        Integer currentAccountId = accountService.getCurrentAccountId();
        VProductionDispatchMain productionDispatchMain = getById(id);
        if (null == productionDispatchMain) {
            throw new NuoNiuException("记录不存在");
        }
        if(!userInfo.getCompanyId().equals(productionDispatchMain.getCompanyId()) || !currentAccountId.equals(productionDispatchMain.getAccountId())){
            throw new NuoNiuException("删除失败，非法操作");
        }
        Integer voucherState = productionDispatchMain.getVoucherState();
        if(voucherState > 0) {
            throw new NuoNiuException("删除失败，已审核 不能删除");
        }
        removeById(id);
        BigDecimal offset = BigDecimal.ZERO.subtract(productionDispatchMain.getDispatchInventoryQuantity());
        //减去 加工单产品明细 累计派遣量
        productionWorkDetailService.updateCumDispatchQuantity(productionDispatchMain.getProductionWorkDetailId(),offset);
    }

    @Override
    public void examine(Long id, Integer status) {
        UserInfo userInfo = UserThreadLocal.get();
        VProductionDispatchMain productionDispatchMain = getById(id);
        if (null == productionDispatchMain) {
            throw new NuoNiuException("操作失败，查询不到记录");
        }
        BigDecimal cumPickQuantity = productionDispatchMain.getCumPickQuantity();
        BigDecimal finishInventoryQuantity = productionDispatchMain.getFinishInventoryQuantity();
        if (status == 0 && (null != finishInventoryQuantity && finishInventoryQuantity.compareTo(BigDecimal.ZERO) > 0) ){
            throw new NuoNiuException("已经开始生产，不能弃审核");
        }
        if (status == 0 ){
            //查询 派工单的 工序汇报记录数量
            int recordNumber = erpWorkProcedureCardDetailService.sumByProductionDispatchId(id);
            if (recordNumber > 0) {
                throw new NuoNiuException("已经开始生产，不能弃审核");
            }
        }
        productionDispatchMain.setVoucherState(status);
        productionDispatchMain.setReviewerDate(LocalDateTime.now());
        productionDispatchMain.setReviewerUserId(userInfo.getUserId());
        productionDispatchMain.setReviewerUserName(userInfo.getRealName());
        updateById(productionDispatchMain);
    }

    @Override
    public int getMaxDispatchTimes(Long workDetailId) {
        UserInfo userInfo = UserThreadLocal.get();
        Integer currentAccountId = accountService.getCurrentAccountId();
        int maxDispatchTimes = baseMapper.getMaxDispatchTimes(userInfo.getCompanyId(), currentAccountId, workDetailId);
        return maxDispatchTimes;
    }

    @Override
    public VProductionDispatchMainVo getByProductionWorkDetailId(Integer productionWorkDetailId) {
        VProductionDispatchMainVo productionDispatchMainVo = new VProductionDispatchMainVo();
        QueryWrapper<VProductionDispatchMain> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("production_work_detail_id",productionWorkDetailId);
        queryWrapper.last("LIMIT 1");
        VProductionDispatchMain dispatchMain = getOne(queryWrapper,false);
        productionDispatchMainVo.setVProductionDispatchMain(dispatchMain);
        if(null != dispatchMain){
            List<VProductionWorkProcedureDetailVo> procedureDetailVoList = procedureDetailService.listByProductionDispatchId(dispatchMain.getId());
            productionDispatchMainVo.setProcedureDetails(procedureDetailVoList);
        }
        return productionDispatchMainVo;
    }
}
